This file is used to analyse the immune cells dataset.

library(dplyr)
library(patchwork)
library(ggplot2)
library(ComplexHeatmap)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

In this section, we set the global settings of the analysis. We will store data there :

save_name = "hfsc"
out_dir = "."

We load the dataset :

sobj = readRDS(paste0(out_dir, "/", save_name, "_sobj.rds"))
sobj
## An object of class Seurat 
## 15384 features across 1454 samples within 1 assay 
## Active assay: RNA (15384 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_24_tsne, RNA_pca_24_umap, harmony, harmony_24_umap, harmony_24_tsne

We load the sample information :

sample_info = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_sample_info.rds"))
project_names_oi = sample_info$project_name

graphics::pie(rep(1, nrow(sample_info)),
              col = sample_info$color,
              labels = sample_info$project_name)

Here are custom colors for each cell type :

color_markers = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_color_markers.rds"))

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank(),
                 axis.text.x = element_text(angle = 30, hjust = 1))

This is the projection of interest :

name2D = "harmony_24_tsne"

We design a custom function to make the GSEA plot and a word cloud graph :

make_gsea_plot = function(gsea_results, gs_oi, fold_change, metric = "FC") {
  fold_change$metric = fold_change[, metric]
  
  plot_list = lapply(gs_oi, FUN = function(gene_set) {
    # Gene set content
    gs_content = gene_sets %>%
      dplyr::filter(gs_name == gene_set) %>%
      dplyr::pull(ensembl_gene) %>%
      unique()
    
    # Gene set size
    nb_genes = length(gs_content)
    
    # Enrichment metrics
    NES = gsea_results@result[gene_set, "NES"]
    p.adjust = gsea_results@result[gene_set, "p.adjust"] %>%
      round(., 4)
    qvalues = gsea_results@result[gene_set, "qvalues"]
    
    if (p.adjust > 0.05) {
      p.adjust = paste0("<span style='color:red;'>", p.adjust, "</span>")
    }
    
    my_subtitle = paste0("\nNES : ", round(NES, 2),
                         " | padj : ", p.adjust,
                         " | qval : ", round(qvalues, 4),
                         " | set size : ", nb_genes, " genes")
    
    # Size limits
    lower_FC = min(fold_change[gs_content, ]$metric, na.rm = TRUE)
    upper_FC = max(fold_change[gs_content, ]$metric, na.rm = TRUE)
    
    # Plot
    p = enrichplot::gseaplot2(x = gsea_results, geneSetID = gene_set) +
      ggplot2::labs(title = gene_set,
                    subtitle = my_subtitle) +
      ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                               margin = ggplot2::margin(3, 3, 5, 3)),
                     plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                              size = 10))
    
    wc = ggplot2::ggplot(fold_change[gs_content, ],
                         aes(label = gene_name, size = abs(metric), color = metric)) +
      ggwordcloud::geom_text_wordcloud_area(show.legend = TRUE) +
      ggplot2::scale_color_gradient2(
        name = metric,
        low = aquarius::color_cnv[1],
        mid = "gray70", midpoint = 0,
        high = aquarius::color_cnv[3]) +
      ggplot2::scale_size_area(max_size = 7) +
      ggplot2::theme_minimal() +
      ggplot2::guides(size = "none")
    
    return(list(p, wc))
  }) %>% unlist(., recursive = FALSE)
  
  return(plot_list)
}

Visualization

Gene expression

We visualize gene expression for some markers :

features = c("percent.mt", "percent.rb", "nFeature_RNA")

plot_list = lapply(features, FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Clusters

We visualize clusters :

cluster_plot = Seurat::DimPlot(sobj, reduction = name2D, label = TRUE) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1)
cluster_plot

We visualize cluster split by sample :

plot_list = aquarius::plot_split_dimred(sobj,
                                        reduction = name2D,
                                        split_by = "project_name",
                                        group_by = "seurat_clusters",
                                        split_color = setNames(sample_info$color,
                                                               nm = sample_info$project_name),
                                        bg_pt_size = 0.5, main_pt_size = 0.5)

plot_list[[length(plot_list) + 1]] = cluster_plot

patchwork::wrap_plots(plot_list, ncol = 4) &
  Seurat::NoLegend()

Analysis

We make a differential expression between each cluster and all others. Then, we compute the differences between HS and HD, all cells considered.

We save the results in a list :

list_results = list()

For the differences between HS and HD, we make GSEA using gene sets from MSigDB :

gene_sets = aquarius::get_gene_sets(species = "Homo sapiens")
gene_sets = gene_sets$gene_sets

head(gene_sets)
## # A tibble: 6 x 16
##   gs_cat gs_subcat gs_name gene_symbol entrez_gene ensembl_gene human_gene_symb~
##   <chr>  <chr>     <chr>   <chr>             <int> <chr>        <chr>           
## 1 C5     GO:BP     GOBP_1~ AASDHPPT          60496 ENSG0000014~ AASDHPPT        
## 2 C5     GO:BP     GOBP_1~ ALDH1L1           10840 ENSG0000014~ ALDH1L1         
## 3 C5     GO:BP     GOBP_1~ ALDH1L2          160428 ENSG0000013~ ALDH1L2         
## 4 C5     GO:BP     GOBP_1~ MTHFD1             4522 ENSG0000010~ MTHFD1          
## 5 C5     GO:BP     GOBP_1~ MTHFD1L           25902 ENSG0000012~ MTHFD1L         
## 6 C5     GO:BP     GOBP_1~ MTHFD2L          441024 ENSG0000016~ MTHFD2L         
## # ... with 9 more variables: human_entrez_gene <int>, human_ensembl_gene <chr>,
## #   gs_id <chr>, gs_pmid <chr>, gs_geoid <chr>, gs_exact_source <chr>,
## #   gs_url <chr>, gs_description <chr>, category <chr>

How many gene sets ?

gene_sets[, c("gs_subcat", "gs_name")] %>%
  dplyr::distinct() %>%
  dplyr::pull(gs_subcat) %>%
  table() %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Category", "Nb gene sets"))
##          Category Nb gene sets
## 1                           50
## 2         CP:KEGG          186
## 3          CP:PID          196
## 4     CP:REACTOME         1615
## 5 CP:WIKIPATHWAYS          664
## 6           GO:BP         7658
## 7           GO:CC         1006
## 8           GO:MF         1738

We get gene name and gene ID correspondence :

gene_corresp = sobj@assays[["RNA"]]@meta.features[, c("gene_name", "Ensembl_ID")] %>%
  `colnames<-`(c("NAME", "ID")) %>%
  dplyr::mutate(ID = as.character(ID))
rownames(gene_corresp) = gene_corresp$ID

head(gene_corresp)
##                       NAME              ID
## ENSG00000238009 AL627309.1 ENSG00000238009
## ENSG00000237491 AL669831.5 ENSG00000237491
## ENSG00000225880  LINC00115 ENSG00000225880
## ENSG00000230368     FAM41C ENSG00000230368
## ENSG00000230699 AL645608.3 ENSG00000230699
## ENSG00000241180 AL645608.5 ENSG00000241180

Clusters identity

Cluster 0

We set the cluster of interest :

clusters_oi = 0
table(sobj$seurat_clusters, sobj$sample_type)[clusters_oi + 1, ]
##  HS  HD 
## 551  41

We visualize these cells on the projection :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = clusters_oi)

What is the cluster identity ?

mark = Seurat::FindMarkers(sobj, ident.1 = clusters_oi)

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(avg_logFC > 0) %>%
  dplyr::filter(abs(pct.1 - pct.2) > 0.2) %>%
  dplyr::arrange(-(pct.1 - pct.2), -avg_logFC)

list_results[[paste0("cluster_", clusters_oi, "_vs_all")]] = mark

dim(mark)
## [1] 126   5
head(mark, n = 20)
##                 p_val avg_logFC pct.1 pct.2    p_val_adj
## CRYAB    3.223852e-94 0.7834540 0.780 0.225 4.959574e-90
## TGFB2    3.180757e-87 0.6612213 0.861 0.357 4.893277e-83
## C2orf40  1.570907e-93 0.7378923 0.833 0.336 2.416683e-89
## LMCD1    7.444640e-85 0.6992374 0.931 0.465 1.145283e-80
## KCNS3    3.317898e-71 0.5291315 0.649 0.188 5.104255e-67
## DKK3     1.191301e-77 0.6433711 0.848 0.390 1.832698e-73
## TUBB2B   2.651137e-77 0.7716228 0.544 0.095 4.078508e-73
## COPZ2    6.014935e-65 0.4994770 0.742 0.300 9.253377e-61
## FGL2     1.085138e-52 0.5080268 0.752 0.327 1.669376e-48
## SCRG1    4.651349e-57 0.5068554 0.716 0.295 7.155636e-53
## TNNT1    8.718882e-59 0.4472881 0.622 0.203 1.341313e-54
## GBP2     1.851852e-55 0.4608426 0.677 0.271 2.848889e-51
## SLC35F3  2.341985e-51 0.4377592 0.590 0.204 3.602910e-47
## NPNT     1.087901e-38 0.2747509 0.654 0.269 1.673626e-34
## TCEAL2   1.045679e-53 0.5214607 0.841 0.458 1.608672e-49
## KRT6B    5.480200e-50 0.5553441 0.838 0.464 8.430739e-46
## MOXD1    3.528316e-47 0.4087822 0.576 0.202 5.427962e-43
## NPPC     4.216031e-45 0.6535122 0.547 0.182 6.485942e-41
## SERPINF1 7.317118e-54 0.5778830 0.497 0.132 1.125665e-49
## IL20RB   3.251850e-43 0.3329731 0.535 0.176 5.002646e-39

We visualize expression for top 9 genes :

plot_list = lapply(rownames(mark)[c(1:9)] %>% na.omit(), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Cluster 1

We set the cluster of interest :

clusters_oi = 1
table(sobj$seurat_clusters, sobj$sample_type)[clusters_oi + 1, ]
##  HS  HD 
## 171  61

We visualize these cells on the projection :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = clusters_oi)

What is the cluster identity ?

mark = Seurat::FindMarkers(sobj, ident.1 = clusters_oi)

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(avg_logFC > 0) %>%
  dplyr::arrange(-(pct.1 - pct.2), -avg_logFC)

list_results[[paste0("cluster_", clusters_oi, "_vs_all")]] = mark

dim(mark)
## [1] 167   5
head(mark, n = 20)
##                 p_val avg_logFC pct.1 pct.2     p_val_adj
## EPCAM    8.701500e-89 0.9414227 0.815 0.208  1.338639e-84
## SAT1     1.470636e-75 0.9821697 0.793 0.220  2.262426e-71
## BHLHE41  8.148751e-80 0.8958304 0.733 0.165  1.253604e-75
## SULT1E1  6.865256e-88 1.0138555 0.694 0.134  1.056151e-83
## SAMD11  6.647504e-111 0.5660124 0.603 0.047 1.022652e-106
## RAMP1    1.757167e-50 0.4707782 0.841 0.295  2.703226e-46
## SLC38A5  2.928872e-51 0.4813625 0.750 0.237  4.505776e-47
## IGFBP2   2.365155e-64 0.7490662 0.720 0.208  3.638554e-60
## SLC7A1   5.693704e-54 0.4669060 0.711 0.204  8.759194e-50
## ADAMTS6  2.327887e-68 0.5062058 0.586 0.099  3.581221e-64
## COL14A1  3.160395e-58 0.5380779 0.638 0.158  4.861951e-54
## WNT5A    9.804365e-61 0.5932221 0.621 0.147  1.508303e-56
## TSPAN18  9.686475e-52 0.5587526 0.668 0.196  1.490167e-47
## GFRA1    2.691764e-58 0.6459056 0.621 0.155  4.141010e-54
## PPFIBP1  1.294996e-55 0.7369063 0.884 0.421  1.992221e-51
## SMS      5.074344e-46 0.4496446 0.664 0.214  7.806370e-42
## PLPP3    2.737337e-48 0.5295214 0.638 0.191  4.211119e-44
## ETS2     1.764498e-67 0.4807835 0.526 0.082  2.714504e-63
## TGFBI    3.159564e-51 0.4474048 0.582 0.138  4.860674e-47
## LGR5     3.366746e-28 0.2740148 0.754 0.311  5.179402e-24

We visualize expression for top 9 genes :

plot_list = lapply(rownames(mark)[c(1:9)] %>% na.omit(), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Cluster 2

We set the cluster of interest :

clusters_oi = 2
table(sobj$seurat_clusters, sobj$sample_type)[clusters_oi + 1, ]
##  HS  HD 
## 136  47

We visualize these cells on the projection :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = clusters_oi)

What is the cluster identity ?

mark = Seurat::FindMarkers(sobj, ident.1 = clusters_oi)

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(avg_logFC > 0) %>%
  dplyr::arrange(-(pct.1 - pct.2), -avg_logFC)

list_results[[paste0("cluster_", clusters_oi, "_vs_all")]] = mark

dim(mark)
## [1] 214   5
head(mark, n = 20)
##                  p_val avg_logFC pct.1 pct.2    p_val_adj
## MGP       2.492025e-97 2.7463894 0.721 0.118 3.833731e-93
## ANGPTL7   1.164052e-91 2.6595942 0.536 0.050 1.790778e-87
## COMP      1.261726e-53 1.7790487 0.634 0.176 1.941039e-49
## EDN2      1.530623e-43 0.9127798 0.563 0.161 2.354710e-39
## BASP1     1.709113e-45 1.2056041 0.760 0.363 2.629300e-41
## TIMP3     4.385138e-68 1.8132960 0.934 0.552 6.746096e-64
## FGF18     6.906960e-40 1.3258349 0.470 0.114 1.062567e-35
## RAMP1     3.724275e-30 0.7112786 0.689 0.338 5.729425e-26
## PYGL      7.123456e-30 0.6808239 0.541 0.197 1.095872e-25
## SLC7A8    3.584080e-31 0.8152063 0.514 0.172 5.513749e-27
## CRLF1     1.039506e-40 0.8020407 0.432 0.092 1.599176e-36
## GJB2      4.866904e-42 1.0132899 0.863 0.526 7.487245e-38
## ISOC1     5.200844e-29 0.6789868 0.525 0.191 8.000978e-25
## LYPD6B    2.226162e-24 0.5716490 0.749 0.415 3.424727e-20
## LGR5      4.278338e-21 0.5745084 0.672 0.340 6.581795e-17
## SRM       9.813331e-32 1.0196308 0.639 0.312 1.509683e-27
## RERG      1.874176e-25 0.5098679 0.486 0.164 2.883232e-21
## ABI3BP    3.885624e-22 0.4800682 0.492 0.179 5.977643e-18
## TNFRSF12A 2.069187e-23 0.7120606 0.568 0.260 3.183237e-19
## PMEPA1    2.103526e-21 0.4925963 0.475 0.182 3.236064e-17

We visualize expression for top 9 genes :

plot_list = lapply(rownames(mark)[c(1:9)] %>% na.omit(), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Cluster 3

We set the cluster of interest :

clusters_oi = 3
table(sobj$seurat_clusters, sobj$sample_type)[clusters_oi + 1, ]
##  HS  HD 
## 103  34

We visualize these cells on the projection :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = clusters_oi)

What is the cluster identity ?

mark = Seurat::FindMarkers(sobj, ident.1 = clusters_oi)

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(avg_logFC > 0) %>%
  dplyr::arrange(-(pct.1 - pct.2), -avg_logFC)

list_results[[paste0("cluster_", clusters_oi, "_vs_all")]] = mark

dim(mark)
## [1] 101   5
head(mark, n = 20)
##                 p_val avg_logFC pct.1 pct.2    p_val_adj
## COL7A1   6.531033e-26 1.2655027 0.723 0.524 1.004734e-21
## ANKRD36C 2.239624e-12 0.8204873 0.372 0.176 3.445438e-08
## VASH1    9.719204e-08 0.7353278 0.394 0.263 1.495202e-03
## PKD1     2.670178e-07 0.5959187 0.372 0.243 4.107802e-03
## GOLGA8B  1.372865e-06 0.7152186 0.314 0.191 2.112016e-02
## ARHGEF10 6.809387e-07 0.7110796 0.372 0.251 1.047556e-02
## NBEAL2   1.502880e-07 0.3438166 0.175 0.062 2.312030e-03
## UBR4     1.096193e-06 0.6459494 0.394 0.282 1.686383e-02
## MTRNR2L1 6.570316e-21 1.1128743 0.774 0.665 1.010777e-16
## XIST     1.251196e-18 1.3722485 0.679 0.574 1.924840e-14
## ITPR2    3.334536e-10 0.8364656 0.562 0.460 5.129850e-06
## PHACTR1  1.820119e-08 0.6825739 0.511 0.415 2.800071e-04
## MACF1    1.814596e-36 1.0909726 0.876 0.782 2.791575e-32
## PTPRF    2.014365e-11 0.7192972 0.606 0.517 3.098899e-07
## SH3PXD2A 3.921949e-10 0.8207961 0.555 0.470 6.033527e-06
## COL12A1  2.171823e-06 0.8041452 0.445 0.361 3.341133e-02
## SMG1     3.945108e-11 0.8516457 0.577 0.495 6.069154e-07
## TNC      2.433094e-15 0.8954344 0.745 0.664 3.743071e-11
## FTX      2.366197e-12 0.9393048 0.613 0.538 3.640157e-08
## PLEC     1.557429e-12 0.7958488 0.628 0.557 2.395949e-08

We visualize expression for top 9 genes :

plot_list = lapply(rownames(mark)[c(1:9)] %>% na.omit(), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Cluster 4

We set the cluster of interest :

clusters_oi = 4
table(sobj$seurat_clusters, sobj$sample_type)[clusters_oi + 1, ]
## HS HD 
## 89 22

We visualize these cells on the projection :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = clusters_oi)

What is the cluster identity ?

mark = Seurat::FindMarkers(sobj, ident.1 = clusters_oi)

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(avg_logFC > 0) %>%
  dplyr::arrange(-(pct.1 - pct.2), -avg_logFC)

list_results[[paste0("cluster_", clusters_oi, "_vs_all")]] = mark

dim(mark)
## [1] 22  5
head(mark, n = 20)
##                p_val avg_logFC pct.1 pct.2    p_val_adj
## ADIRF   4.807155e-08 0.5998750 0.568 0.360 7.395327e-04
## ID2     5.650554e-07 0.3653986 0.333 0.163 8.692812e-03
## RPL36AL 4.539333e-09 0.3379842 0.955 0.853 6.983310e-05
## S100A6  2.593260e-07 0.3830809 0.991 0.902 3.989470e-03
## PFDN5   1.447512e-07 0.2873697 0.973 0.888 2.226853e-03
## TOMM7   1.298082e-14 0.3636187 0.982 0.904 1.996970e-10
## RPS5    6.906764e-12 0.2710716 1.000 0.934 1.062537e-07
## FAU     5.929448e-14 0.2813693 1.000 0.941 9.121863e-10
## RPL21   1.343234e-09 0.2768561 0.991 0.937 2.066431e-05
## RPS10   1.880413e-06 0.2884676 0.973 0.920 2.892828e-02
## KRT15   3.648370e-09 0.4237135 1.000 0.951 5.612653e-05
## RPL35   1.669337e-08 0.2660652 0.991 0.948 2.568109e-04
## RPL9    9.792595e-12 0.2765172 0.991 0.949 1.506493e-07
## RPS12   2.526939e-09 0.2635716 1.000 0.961 3.887443e-05
## RPS27A  3.801623e-11 0.2529029 1.000 0.961 5.848416e-07
## RPL14   1.069987e-12 0.3068006 1.000 0.963 1.646067e-08
## SNHG8   9.948208e-07 0.2825229 0.883 0.846 1.530432e-02
## RPL30   4.893146e-11 0.2525618 1.000 0.967 7.527617e-07
## RPL13A  7.599923e-09 0.2706194 1.000 0.971 1.169172e-04
## RPL34   8.772978e-13 0.3301666 1.000 0.973 1.349635e-08

We visualize expression for top 9 genes :

plot_list = lapply(rownames(mark)[c(1:9)] %>% na.omit(), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Cluster 5

We set the cluster of interest :

clusters_oi = 5
table(sobj$seurat_clusters, sobj$sample_type)[clusters_oi + 1, ]
## HS HD 
## 43 21

We visualize these cells on the projection :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = clusters_oi)

What is the cluster identity ?

mark = Seurat::FindMarkers(sobj, ident.1 = clusters_oi)

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(avg_logFC > 0) %>%
  dplyr::arrange(-(pct.1 - pct.2), -avg_logFC)

list_results[[paste0("cluster_", clusters_oi, "_vs_all")]] = mark

dim(mark)
## [1] 170   5
head(mark, n = 20)
##                p_val avg_logFC pct.1 pct.2    p_val_adj
## KRT75   4.261481e-82 2.6425233 0.875 0.101 6.555862e-78
## SULF2   4.517836e-37 0.6274183 0.750 0.147 6.950239e-33
## CD24    7.950250e-44 0.7102213 0.672 0.102 1.223066e-39
## ALCAM   3.398661e-49 0.8414933 0.641 0.079 5.228500e-45
## NECTIN4 1.132459e-71 0.5006038 0.578 0.035 1.742176e-67
## DSG1    1.582596e-67 0.9318441 0.578 0.040 2.434665e-63
## ALDH2   8.100503e-28 0.8544061 0.891 0.369 1.246181e-23
## COL27A1 8.911981e-19 0.6993912 0.781 0.278 1.371019e-14
## TUBA4A  3.929171e-21 0.6956015 0.766 0.264 6.044636e-17
## NRP2    8.392743e-28 0.8803858 0.625 0.144 1.291140e-23
## CUX1    6.035228e-27 0.9231917 0.625 0.148 9.284595e-23
## NOTCH3  2.092677e-50 0.3759649 0.516 0.043 3.219374e-46
## CLDN1   6.364765e-15 0.6859477 0.812 0.343 9.791555e-11
## TFAP2B  6.792002e-17 0.6717193 0.875 0.413 1.044882e-12
## ZNF750  2.895948e-60 0.5804328 0.484 0.029 4.455126e-56
## RHOV    1.378067e-59 0.6346190 0.484 0.030 2.120018e-55
## HOPX    7.308344e-18 0.8920688 0.859 0.407 1.124316e-13
## LGR5    3.940750e-11 0.3233861 0.812 0.362 6.062450e-07
## TM4SF1  1.472294e-20 1.0866761 0.859 0.413 2.264977e-16
## SEPT11  5.175101e-19 0.7978541 0.797 0.352 7.961375e-15

We visualize expression for top 9 genes :

plot_list = lapply(rownames(mark)[c(1:9)] %>% na.omit(), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Cluster 6

We set the cluster of interest :

clusters_oi = 6
table(sobj$seurat_clusters, sobj$sample_type)[clusters_oi + 1, ]
## HS HD 
## 41 12

We visualize these cells on the projection :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = clusters_oi)

What is the cluster identity ?

mark = Seurat::FindMarkers(sobj, ident.1 = clusters_oi)

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(avg_logFC > 0) %>%
  dplyr::arrange(-(pct.1 - pct.2), -avg_logFC)

list_results[[paste0("cluster_", clusters_oi, "_vs_all")]] = mark

dim(mark)
## [1] 379   5
head(mark, n = 20)
##                p_val avg_logFC pct.1 pct.2    p_val_adj
## NOTCH1  3.494599e-43 1.2124688 0.736 0.124 5.376092e-39
## FLNB    8.873739e-33 1.7683509 0.906 0.344 1.365136e-28
## NOTCH3  4.668186e-60 1.0439873 0.585 0.044 7.181538e-56
## GRHL1   1.093699e-37 1.4300971 0.642 0.105 1.682547e-33
## CMYA5   1.222744e-24 1.5442306 0.717 0.218 1.881070e-20
## FAM129A 5.322588e-40 1.0668158 0.547 0.064 8.188270e-36
## ABI3BP  1.448411e-19 1.0344985 0.679 0.201 2.228235e-15
## MTSS1   1.737633e-25 1.2858094 0.868 0.390 2.673174e-21
## SFXN5   8.051626e-29 0.9838617 0.585 0.111 1.238662e-24
## MRC2    1.106825e-24 1.6737178 0.830 0.383 1.702739e-20
## PLEKHG3 1.415801e-20 1.0646931 0.642 0.200 2.178068e-16
## CNKSR3  6.452408e-29 1.1084106 0.528 0.088 9.926384e-25
## NECTIN4 5.497905e-42 0.9531383 0.472 0.043 8.457977e-38
## SULF2   5.034367e-21 1.1926350 0.585 0.158 7.744870e-17
## STON2   5.577013e-19 0.8646229 0.585 0.171 8.579676e-15
## FABP5   6.371424e-22 1.6432462 0.849 0.438 9.801798e-18
## PDZRN3  2.943543e-24 0.8262355 0.491 0.089 4.528346e-20
## DIAPH2  5.071946e-17 0.9115014 0.623 0.224 7.802682e-13
## CASZ1   5.112385e-18 1.0620881 0.774 0.376 7.864892e-14
## CELSR2  1.075819e-18 1.2863026 0.736 0.341 1.655040e-14

We visualize expression for top 9 genes :

plot_list = lapply(rownames(mark)[c(1:9)] %>% na.omit(), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Cluster 7

We set the cluster of interest :

clusters_oi = 7
table(sobj$seurat_clusters, sobj$sample_type)[clusters_oi + 1, ]
## HS HD 
## 41  2

We visualize these cells on the projection :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = clusters_oi)

What is the cluster identity ?

mark = Seurat::FindMarkers(sobj, ident.1 = clusters_oi)

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(avg_logFC > 0) %>%
  dplyr::arrange(-(pct.1 - pct.2), -avg_logFC)

list_results[[paste0("cluster_", clusters_oi, "_vs_all")]] = mark

dim(mark)
## [1] 17  5
head(mark, n = 20)
##                p_val avg_logFC pct.1 pct.2    p_val_adj
## SCGB2A2 7.647401e-44 2.3207587 0.558 0.048 1.176476e-39
## CAMK2N1 1.635715e-17 0.5294409 0.465 0.082 2.516384e-13
## NPPC    1.790106e-12 1.6218556 0.698 0.320 2.753898e-08
## NPTX2   2.111608e-10 0.4160402 0.535 0.164 3.248498e-06
## KRT6C   7.926649e-09 0.4073147 0.535 0.185 1.219436e-04
## POSTN   9.707565e-13 1.7186060 0.814 0.468 1.493412e-08
## PCDH7   1.515280e-06 0.5112558 0.581 0.240 2.331106e-02
## TIMP1   1.322974e-06 0.3892821 0.744 0.432 2.035262e-02
## PLCB1   1.938641e-06 0.2896887 0.488 0.187 2.982406e-02
## CHI3L1  1.150496e-06 0.9322679 0.535 0.242 1.769922e-02
## ADIRF   1.828850e-06 0.6303150 0.651 0.368 2.813503e-02
## RFLNA   2.732072e-06 0.2655561 0.372 0.116 4.203020e-02
## NR4A1   6.133495e-08 0.4890826 0.326 0.085 9.435768e-04
## HLA-DRA 5.218463e-09 1.1929512 0.233 0.043 8.028083e-05
## CDC25B  1.291361e-07 0.2835974 0.233 0.047 1.986630e-03
## ZFHX4   2.515833e-06 0.3477340 0.116 0.016 3.870357e-02
## S100A6  2.397557e-08 0.5459797 0.977 0.907 3.688402e-04

We visualize expression for top 9 genes :

plot_list = lapply(rownames(mark)[c(1:9)] %>% na.omit(), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Cluster 8

We set the cluster of interest :

clusters_oi = 8
table(sobj$seurat_clusters, sobj$sample_type)[clusters_oi + 1, ]
## HS HD 
## 37  2

We visualize these cells on the projection :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = clusters_oi)

What is the cluster identity ?

mark = Seurat::FindMarkers(sobj, ident.1 = clusters_oi)

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(avg_logFC > 0) %>%
  dplyr::arrange(-(pct.1 - pct.2), -avg_logFC)

list_results[[paste0("cluster_", clusters_oi, "_vs_all")]] = mark

dim(mark)
## [1] 101   5
head(mark, n = 20)
##                 p_val avg_logFC pct.1 pct.2    p_val_adj
## PTHLH    2.299174e-94 1.4808986 0.897 0.052 3.537049e-90
## AQP3     2.438416e-32 1.8302392 1.000 0.297 3.751259e-28
## FST      2.061507e-26 1.2373030 0.949 0.254 3.171422e-22
## TMEM45A  4.320274e-27 0.9680393 0.769 0.167 6.646310e-23
## C1QTNF12 4.390215e-19 0.7919568 0.821 0.243 6.753907e-15
## VIM      1.969431e-22 1.4872649 0.923 0.360 3.029773e-18
## CRABP2   4.165189e-18 0.7416611 0.744 0.208 6.407726e-14
## ANKH     4.422814e-18 0.5728452 0.718 0.188 6.804057e-14
## C19orf33 1.099493e-13 0.7194400 0.872 0.356 1.691460e-09
## ODC1     4.704920e-21 0.6689324 0.641 0.128 7.238048e-17
## CYR61    6.040557e-19 0.5846073 0.641 0.146 9.292794e-15
## RND3     1.805197e-15 0.6701467 0.692 0.208 2.777115e-11
## MOXD1    8.470459e-14 0.6805826 0.821 0.341 1.303095e-09
## SIRT2    1.707509e-10 0.4731114 0.821 0.362 2.626832e-06
## BDNF     8.552560e-14 0.5160020 0.615 0.162 1.315726e-09
## C16orf74 1.435042e-15 0.4744279 0.590 0.140 2.207669e-11
## TNFRSF18 3.500544e-15 0.4155453 0.590 0.140 5.385237e-11
## TPD52L1  3.573035e-11 0.3931087 0.692 0.242 5.496757e-07
## ABRACL   1.468416e-14 0.7629582 0.949 0.519 2.259011e-10
## SOCS3    3.094980e-08 0.3219107 0.692 0.265 4.761317e-04

We visualize expression for top 9 genes :

plot_list = lapply(rownames(mark)[c(1:9)] %>% na.omit(), FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

HS vs HD

We change the cells identity :

Seurat::Idents(sobj) = sobj$sample_type
table(Seurat::Idents(sobj))
## 
##   HS   HD 
## 1212  242

We compare all cells between HS and HD :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = "HS", group2 = "HD")

Differential expression

We make a differential expression analysis between cells from HS and cells from HD :

mark = Seurat::FindMarkers(sobj, ident.1 = "HS", ident.2 = "HD")

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05)

list_results[["HS_vs_HD"]] = mark

How many differentially expressed genes by condition ?

# Enriched in HS
mark %>%
  dplyr::filter(avg_logFC > 0) %>%
  nrow()
## [1] 106
# Enriched in HD
mark %>%
  dplyr::filter(avg_logFC < 0) %>%
  nrow()
## [1] 117

There are no significantly enriched genes in HD compared to HS. What are the genes up-regulated in HS ?

genes = mark %>%
  dplyr::filter(avg_logFC > 0) %>%
  rownames %>%
  sort()

genes
##   [1] "ABRACL"    "AQP3"      "ARF5"      "ART3"      "ATP6V0E1"  "B2M"      
##   [7] "C1QTNF12"  "C1R"       "C2orf40"   "CCL2"      "CD81"      "CD9"      
##  [13] "CDC42EP3"  "CHI3L1"    "CISD1"     "CNN3"      "COL8A1"    "COMMD6"   
##  [19] "COMT"      "CRYAB"     "CSAD"      "CSTB"      "CYP1B1"    "DHCR24"   
##  [25] "DKK3"      "EEF1A1"    "FAM13A"    "FTH1"      "FTL"       "GBP2"     
##  [31] "GCLM"      "HIF1A"     "HLA-A"     "HLA-C"     "HSPA1A"    "HSPA1B"   
##  [37] "IFITM3"    "IL20RB"    "JUNB"      "KRT6B"     "KRTCAP2"   "LGALS1"   
##  [43] "LGALS7"    "LMCD1"     "LSAMP"     "MIF"       "MOXD1"     "MSRB2"    
##  [49] "MT-CO1"    "MT-CO2"    "MTRNR2L1"  "MTRNR2L10" "MTRNR2L12" "MTRNR2L8" 
##  [55] "MTURN"     "NACA"      "NDUFB2"    "NDUFB3"    "NPNT"      "NPPC"     
##  [61] "OST4"      "PCMTD1"    "PFN1"      "PLCG2"     "PLIN2"     "PPIA"     
##  [67] "PRNP"      "PYCARD"    "RPL12"     "RPL13"     "RPL18A"    "RPL23A"   
##  [73] "RPL28"     "RPL34"     "RPL7A"     "RPL8"      "RPS14"     "RPS19"    
##  [79] "RPS26"     "RPS27"     "RPS28"     "RPS3"      "RPS4Y1"    "RPS7"     
##  [85] "RPS9"      "S100A13"   "S100A6"    "SBSPON"    "SEMA3C"    "SERPINF1" 
##  [91] "SLC25A6"   "SLC35F3"   "SMDT1"     "SOCS3"     "SOD2"      "SPTSSA"   
##  [97] "SSFA2"     "TENM4"     "TGFB2"     "TMEM163"   "TNNT1"     "TPT1"     
## [103] "TSPO"      "TUBB2B"    "UQCRH"     "WIF1"

Heatmap

We visualize the expression of these genes on a heatmap.

features_oi = genes

length(features_oi)
## [1] 106

We prepare the scaled expression matrix :

mat_expression = Seurat::GetAssayData(sobj, assay = "RNA", slot = "data")[features_oi, ]
mat_expression = Matrix::t(mat_expression)
mat_expression = dynutils::scale_quantile(mat_expression) # between 0 and 1
mat_expression = Matrix::t(mat_expression)
mat_expression = as.matrix(mat_expression) # not sparse

dim(mat_expression)
## [1]  106 1454

We prepare the heatmap annotation :

ha_top = ComplexHeatmap::HeatmapAnnotation(
  sample_type = sobj$sample_type,
  cluster = sobj$seurat_clusters,
  col = list(sample_type = setNames(nm = c("HS", "HD"),
                                    c("#C55F40", "#2C78E6")),
             cluster = setNames(nm = levels(sobj$seurat_clusters),
                                aquarius::gg_color_hue(length(levels(sobj$seurat_clusters))))))

And the heatmap :

sobj$cell_group = paste0(sobj$sample_type, "_", sobj$seurat_clusters) %>%
  as.factor()

ht = ComplexHeatmap::Heatmap(mat_expression,
                             col = aquarius::color_cnv,
                             # Annotation
                             top_annotation = ha_top,
                             # Grouping
                             column_order = sobj@meta.data %>%
                               dplyr::arrange(sample_type, seurat_clusters) %>%
                               rownames(),
                             column_split = sobj$cell_group,
                             column_gap = unit.c(rep(unit(0.01, "mm"), 8),
                                                 unit(2, "mm"),
                                                 rep(unit(0.01, "mm"), 8)),
                             column_title = NULL,
                             cluster_rows = TRUE,
                             cluster_columns = FALSE,
                             show_column_names = FALSE,
                             # Visual aspect
                             show_heatmap_legend = TRUE,
                             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

GSEA

We run a GSEA for all gene sets, from the full count matrix :

ranked_gene_list = aquarius::run_foldchange(Seurat::GetAssayData(sobj, assay = "RNA", slot = "counts"),
                                            group1 = colnames(sobj)[sobj@active.ident %in% "HS"],
                                            group2 = colnames(sobj)[sobj@active.ident %in% "HD"])
names(ranked_gene_list) = gene_corresp$ID

gsea_results = aquarius::gsea_run(ranked_gene_list = ranked_gene_list,
                                  gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                  GSEA_p_val_thresh = 1)

list_results[["HS_vs_HD_gsea"]] = gsea_results

gsea_results@result %>%
  dplyr::filter(pvalue < 0.05) %>%
  dplyr::top_n(., n = 200, wt = abs(NES)) %>%
  dplyr::mutate(too_long = ifelse(nchar(ID) > 60, yes = TRUE, no = FALSE)) %>%
  dplyr::mutate(ID = stringr::str_sub(ID, end = 60)) %>%
  dplyr::mutate(ID = ifelse(too_long, yes = paste0(ID, "..."), no = ID)) %>%
  aquarius::gsea_plot(show_legend = TRUE) +
  ggplot2::labs(title = "GSEA using all genes (count matrix)") +
  ggplot2::theme(plot.title = element_text(size = 20))

We compute a fold change table to make a wordcloud :

fold_change = gene_corresp
colnames(fold_change) = c("gene_name", "ID")
rownames(fold_change) = fold_change$ID
fold_change$FC = ranked_gene_list[rownames(fold_change)]
fold_change$pct.1 = Seurat::GetAssayData(sobj, assay = "RNA", slot = "counts")[, sobj@active.ident == "HS"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$pct.2 = Seurat::GetAssayData(sobj, assay = "RNA", slot = "counts")[, sobj@active.ident == "HD"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$FC_x_pct = ifelse(fold_change$FC > 0,
                              yes = fold_change$FC * fold_change$pct.1,
                              no = fold_change$FC * fold_change$pct.2)

dim(fold_change) ; head(fold_change)
## [1] 15384     6
##                  gene_name              ID          FC       pct.1       pct.2
## ENSG00000238009 AL627309.1 ENSG00000238009 -0.90020462 0.004950495 0.004132231
## ENSG00000237491 AL669831.5 ENSG00000237491  0.25326986 0.079207921 0.049586777
## ENSG00000225880  LINC00115 ENSG00000225880 -0.48516712 0.032178218 0.033057851
## ENSG00000230368     FAM41C ENSG00000230368  0.28238680 0.085808581 0.061983471
## ENSG00000230699 AL645608.3 ENSG00000230699 -0.02948763 0.023102310 0.016528926
## ENSG00000241180 AL645608.5 ENSG00000241180 -1.48516712 0.004950495 0.008264463
##                      FC_x_pct
## ENSG00000238009 -0.0037198538
## ENSG00000237491  0.0200609793
## ENSG00000225880 -0.0160385824
## ENSG00000230368  0.0242312103
## ENSG00000230699 -0.0004873989
## ENSG00000241180 -0.0122741084

We make the gsea plot for some gene sets :

gs_oi = c("GOMF_STRUCTURAL_CONSTITUENT_OF_RIBOSOME",
          "GOBP_B_CELL_CHEMOTAXIS",
          "GOBP_AUTOCRINE_SIGNALING",
          "GOBP_COMPLEMENT_ACTIVATION",
          "GOBP_CORNIFICATION",
          "WP_HAIR_FOLLICLE_DEVELOPMENT_ORGANOGENESIS_PART_2_OF_3",
          "PID_INTEGRIN3_PATHWAY")

plot_list = make_gsea_plot(gsea_results, gs_oi, fold_change, "FC_x_pct")

patchwork::wrap_plots(plot_list, ncol = 2, widths = c(2,1.5))

Save

We save the list of results :

saveRDS(list_results, file = paste0(out_dir, "/", save_name, "_list_results.rds"))

R Session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
## [1] grid      stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
## [1] ComplexHeatmap_2.14.0 ggplot2_3.3.5         patchwork_1.1.2      
## [4] dplyr_1.0.7          
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 vctrs_0.3.8                
##   [7] usethis_2.0.1               dynwrap_1.2.1              
##   [9] blob_1.2.1                  survival_3.2-13            
##  [11] prodlim_2019.11.13          dynutils_1.0.5             
##  [13] later_1.3.0                 DBI_1.1.1                  
##  [15] R.utils_2.11.0              SingleCellExperiment_1.8.0 
##  [17] rappdirs_0.3.3              uwot_0.1.8                 
##  [19] dqrng_0.2.1                 jpeg_0.1-8.1               
##  [21] zlibbioc_1.32.0             pspline_1.0-18             
##  [23] pcaMethods_1.78.0           mvtnorm_1.1-1              
##  [25] htmlwidgets_1.5.4           GlobalOptions_0.1.2        
##  [27] future_1.22.1               UpSetR_1.4.0               
##  [29] laeken_0.5.2                leiden_0.3.3               
##  [31] clustree_0.4.3              parallel_3.6.3             
##  [33] scater_1.14.6               irlba_2.3.3                
##  [35] markdown_1.1                DEoptimR_1.0-9             
##  [37] tidygraph_1.1.2             Rcpp_1.0.9                 
##  [39] readr_2.0.2                 KernSmooth_2.23-17         
##  [41] carrier_0.1.0               promises_1.1.0             
##  [43] gdata_2.18.0                DelayedArray_0.12.3        
##  [45] limma_3.42.2                graph_1.64.0               
##  [47] RcppParallel_5.1.4          Hmisc_4.4-0                
##  [49] fs_1.5.2                    RSpectra_0.16-0            
##  [51] fastmatch_1.1-0             ranger_0.12.1              
##  [53] digest_0.6.25               png_0.1-7                  
##  [55] sctransform_0.2.1           cowplot_1.0.0              
##  [57] DOSE_3.12.0                 here_1.0.1                 
##  [59] TInGa_0.0.0.9000            ggraph_2.0.3               
##  [61] pkgconfig_2.0.3             GO.db_3.10.0               
##  [63] DelayedMatrixStats_1.8.0    gower_0.2.1                
##  [65] ggbeeswarm_0.6.0            iterators_1.0.12           
##  [67] DropletUtils_1.6.1          reticulate_1.26            
##  [69] clusterProfiler_3.14.3      SummarizedExperiment_1.16.1
##  [71] circlize_0.4.15             beeswarm_0.4.0             
##  [73] GetoptLong_1.0.5            xfun_0.35                  
##  [75] bslib_0.3.1                 zoo_1.8-10                 
##  [77] tidyselect_1.1.0            reshape2_1.4.4             
##  [79] purrr_0.3.4                 ica_1.0-2                  
##  [81] pcaPP_1.9-73                viridisLite_0.3.0          
##  [83] rtracklayer_1.46.0          rlang_1.0.2                
##  [85] hexbin_1.28.1               jquerylib_0.1.4            
##  [87] dyneval_0.9.9               glue_1.4.2                 
##  [89] RColorBrewer_1.1-2          matrixStats_0.56.0         
##  [91] stringr_1.4.0               lava_1.6.7                 
##  [93] europepmc_0.3               DESeq2_1.26.0              
##  [95] recipes_0.1.17              labeling_0.3               
##  [97] httpuv_1.5.2                class_7.3-17               
##  [99] BiocNeighbors_1.4.2         DO.db_2.9                  
## [101] annotate_1.64.0             jsonlite_1.7.2             
## [103] XVector_0.26.0              bit_4.0.4                  
## [105] mime_0.9                    aquarius_0.1.5             
## [107] Rsamtools_2.2.3             gridExtra_2.3              
## [109] gplots_3.0.3                stringi_1.4.6              
## [111] processx_3.5.2              gsl_2.1-6                  
## [113] bitops_1.0-6                cli_3.0.1                  
## [115] batchelor_1.2.4             RSQLite_2.2.0              
## [117] randomForest_4.6-14         tidyr_1.1.4                
## [119] data.table_1.14.2           rstudioapi_0.13            
## [121] org.Mm.eg.db_3.10.0         GenomicAlignments_1.22.1   
## [123] nlme_3.1-147                qvalue_2.18.0              
## [125] scran_1.14.6                locfit_1.5-9.4             
## [127] scDblFinder_1.1.8           listenv_0.8.0              
## [129] ggthemes_4.2.4              gridGraphics_0.5-0         
## [131] R.oo_1.24.0                 dbplyr_1.4.4               
## [133] BiocGenerics_0.32.0         TTR_0.24.2                 
## [135] readxl_1.3.1                lifecycle_1.0.1            
## [137] timeDate_3043.102           ggpattern_0.3.1            
## [139] munsell_0.5.0               cellranger_1.1.0           
## [141] R.methodsS3_1.8.1           proxyC_0.1.5               
## [143] visNetwork_2.0.9            caTools_1.18.0             
## [145] codetools_0.2-16            ggwordcloud_0.5.0          
## [147] Biobase_2.46.0              GenomeInfoDb_1.22.1        
## [149] vipor_0.4.5                 lmtest_0.9-38              
## [151] msigdbr_7.5.1               htmlTable_1.13.3           
## [153] triebeard_0.3.0             lsei_1.2-0                 
## [155] xtable_1.8-4                ROCR_1.0-7                 
## [157] BiocManager_1.30.10         scatterplot3d_0.3-41       
## [159] abind_1.4-5                 farver_2.0.3               
## [161] parallelly_1.28.1           RANN_2.6.1                 
## [163] askpass_1.1                 GenomicRanges_1.38.0       
## [165] RcppAnnoy_0.0.16            tibble_3.1.5               
## [167] ggdendro_0.1-20             cluster_2.1.0              
## [169] future.apply_1.5.0          Seurat_3.1.5               
## [171] dendextend_1.15.1           Matrix_1.3-2               
## [173] ellipsis_0.3.2              prettyunits_1.1.1          
## [175] lubridate_1.7.9             ggridges_0.5.2             
## [177] igraph_1.2.5                RcppEigen_0.3.3.7.0        
## [179] fgsea_1.12.0                remotes_2.4.2              
## [181] scBFA_1.0.0                 destiny_3.0.1              
## [183] VIM_6.1.1                   testthat_3.1.0             
## [185] htmltools_0.5.2             BiocFileCache_1.10.2       
## [187] yaml_2.2.1                  utf8_1.1.4                 
## [189] plotly_4.9.2.1              XML_3.99-0.3               
## [191] ModelMetrics_1.2.2.2        e1071_1.7-3                
## [193] foreign_0.8-76              withr_2.5.0                
## [195] fitdistrplus_1.0-14         BiocParallel_1.20.1        
## [197] xgboost_1.4.1.1             bit64_4.0.5                
## [199] foreach_1.5.0               robustbase_0.93-9          
## [201] Biostrings_2.54.0           GOSemSim_2.13.1            
## [203] rsvd_1.0.3                  memoise_2.0.0              
## [205] evaluate_0.18               forcats_0.5.0              
## [207] rio_0.5.16                  geneplotter_1.64.0         
## [209] tzdb_0.1.2                  caret_6.0-86               
## [211] ps_1.6.0                    DiagrammeR_1.0.6.1         
## [213] curl_4.3                    fdrtool_1.2.15             
## [215] fansi_0.4.1                 highr_0.8                  
## [217] urltools_1.7.3              xts_0.12.1                 
## [219] GSEABase_1.48.0             acepack_1.4.1              
## [221] edgeR_3.28.1                checkmate_2.0.0            
## [223] scds_1.2.0                  cachem_1.0.6               
## [225] npsurv_0.4-0                babelgene_22.3             
## [227] rjson_0.2.20                openxlsx_4.1.5             
## [229] ggrepel_0.9.1               clue_0.3-60                
## [231] rprojroot_2.0.2             stabledist_0.7-1           
## [233] tools_3.6.3                 sass_0.4.0                 
## [235] nichenetr_1.1.1             magrittr_2.0.1             
## [237] RCurl_1.98-1.2              proxy_0.4-24               
## [239] car_3.0-11                  ape_5.3                    
## [241] ggplotify_0.0.5             xml2_1.3.2                 
## [243] httr_1.4.2                  assertthat_0.2.1           
## [245] rmarkdown_2.18              boot_1.3-25                
## [247] globals_0.14.0              R6_2.4.1                   
## [249] Rhdf5lib_1.8.0              nnet_7.3-14                
## [251] RcppHNSW_0.2.0              progress_1.2.2             
## [253] genefilter_1.68.0           statmod_1.4.34             
## [255] gtools_3.8.2                shape_1.4.6                
## [257] HDF5Array_1.14.4            BiocSingular_1.2.2         
## [259] rhdf5_2.30.1                splines_3.6.3              
## [261] AUCell_1.8.0                carData_3.0-4              
## [263] colorspace_1.4-1            generics_0.1.0             
## [265] stats4_3.6.3                base64enc_0.1-3            
## [267] dynfeature_1.0.0            smoother_1.1               
## [269] gridtext_0.1.1              pillar_1.6.3               
## [271] tweenr_1.0.1                sp_1.4-1                   
## [273] ggplot.multistats_1.0.0     rvcheck_0.1.8              
## [275] GenomeInfoDbData_1.2.2      plyr_1.8.6                 
## [277] gtable_0.3.0                zip_2.2.0                  
## [279] knitr_1.41                  latticeExtra_0.6-29        
## [281] biomaRt_2.42.1              IRanges_2.20.2             
## [283] fastmap_1.1.0               ADGofTest_0.3              
## [285] copula_1.0-0                doParallel_1.0.15          
## [287] AnnotationDbi_1.48.0        vcd_1.4-8                  
## [289] babelwhale_1.0.1            openssl_1.4.1              
## [291] scales_1.1.1                backports_1.2.1            
## [293] S4Vectors_0.24.4            ipred_0.9-12               
## [295] enrichplot_1.6.1            hms_1.1.1                  
## [297] ggforce_0.3.1               Rtsne_0.15                 
## [299] shiny_1.7.1                 numDeriv_2016.8-1.1        
## [301] polyclip_1.10-0             lazyeval_0.2.2             
## [303] Formula_1.2-3               tsne_0.1-3                 
## [305] crayon_1.3.4                MASS_7.3-54                
## [307] pROC_1.16.2                 viridis_0.5.1              
## [309] dynparam_1.0.0              rpart_4.1-15               
## [311] zinbwave_1.8.0              compiler_3.6.3             
## [313] ggtext_0.1.0
LS0tCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJab29tIGluIGhhaXIgZm9sbGljbGUgc3RlbSBjZWxscyAoSEYtU0NzKSIKYXV0aG9yOiAiQXVkcmV5IgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclWS0lbS0lZCcpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCi0tLQoKPHN0eWxlPgpib2R5IHsKdGV4dC1hbGlnbjoganVzdGlmeX0KPC9zdHlsZT4KCjwhLS0gQXV0b21hdGljYWxseSBjb21wdXRlcyBhbmQgcHJpbnRzIGluIHRoZSBvdXRwdXQgdGhlIHJ1bm5pbmcgdGltZSBmb3IgYW55IGNvZGUgY2h1bmsgLS0+CmBgYHtyLCBlY2hvPUZBTFNFfQojIGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL3JtYXJrZG93bi9pc3N1ZXMvMTQ1Mwpob29rcyA9IGtuaXRyOjprbml0X2hvb2tzJGdldCgpCmhvb2tfZm9sZGFibGUgPSBmdW5jdGlvbih0eXBlKSB7CiAgZm9yY2UodHlwZSkKICBmdW5jdGlvbih4LCBvcHRpb25zKSB7CiAgICByZXMgPSBob29rc1tbdHlwZV1dKHgsIG9wdGlvbnMpCiAgICAKICAgIGlmIChpc0ZBTFNFKG9wdGlvbnNbW3Bhc3RlMCgiZm9sZF8iLCB0eXBlKV1dKSkgcmV0dXJuKHJlcykKICAgIAogICAgcGFzdGUwKAogICAgICAiPGRldGFpbHM+PHN1bW1hcnk+IiwgInNob3ciLCAiPC9zdW1tYXJ5PlxuXG4iLAogICAgICByZXMsCiAgICAgICJcblxuPC9kZXRhaWxzPiIKICAgICkKICB9Cn0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KAogIG91dHB1dCA9IGhvb2tfZm9sZGFibGUoIm91dHB1dCIpLAogIHBsb3QgPSBob29rX2ZvbGRhYmxlKCJwbG90IiksCiAgdGltZV9pdCA9IGxvY2FsKHsKICAgIG5vdyA9IE5VTEwKICAgIGZ1bmN0aW9uKGJlZm9yZSwgb3B0aW9ucykgewogICAgICBpZiAob3B0aW9ucyR0aW1lX2l0KSB7CiAgICAgICAgaWYgKGJlZm9yZSkgewogICAgICAgICAgbm93IDw9IFN5cy50aW1lKCkKICAgICAgICB9IGVsc2UgewogICAgICAgICAgcmVzID0gZGlmZnRpbWUoU3lzLnRpbWUoKSwgbm93LCB1bml0cyA9ICJzZWNzIikKICAgICAgICAgIHBhc3RlKCIoVGltZSB0byBydW4gOiIsIHJvdW5kKHJlcywgZGlnaXRzID0gMiksICJzKSIpCiAgICAgICAgfQogICAgICB9CiAgICB9CiAgfSkKKQpgYGAKCjwhLS0gU2V0IGRlZmF1bHQgcGFyYW1ldGVycyBmb3IgYWxsIGNodW5rcyAtLT4KYGBge3IsIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CnNldC5zZWVkKDEzMzdMKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICMgZGlzcGxheSBjb2RlCiAgICAgICAgICAgICAgICAgICAgICAjIGRpc3BsYXkgY2h1bmsgb3V0cHV0CiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBmb2xkX291dHB1dCA9IEZBTFNFLCAjIHVzZWZ1bGwgZm9yIHNlc3Npb25JbmZvKCkKICAgICAgICAgICAgICAgICAgICAgIGZvbGRfcGxvdCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAjIGZpZ3VyZSBzZXR0aW5ncwogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gJ2NlbnRlcicsCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSAyMCwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSAxNSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBzb21ldGhpbmcgYWJvdXQgc2VlZCwgY2h1bmsgYW5kIFJtYXJrZG93biBjb21waWxhdGlvbgogICAgICAgICAgICAgICAgICAgICAgIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zOTQxNzAwMy9sb25nLXZlY3RvcnMtbm90LXN1cHBvcnRlZC15ZXQtZXJyb3ItaW4tcm1kLWJ1dC1ub3QtaW4tci1zY3JpcHQKICAgICAgICAgICAgICAgICAgICAgICMgY2FjaGUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgY2FjaGUubGF6eSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBhZGQgcnVudGltZSBhZnRlciBjaHVuawogICAgICAgICAgICAgICAgICAgICAgdGltZV9pdCA9IEZBTFNFKQpgYGAKCgpUaGlzIGZpbGUgaXMgdXNlZCB0byBhbmFseXNlIHRoZSBpbW11bmUgY2VsbHMgZGF0YXNldC4KCmBgYHtyIGxpYnJhcnl9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCgoubGliUGF0aHMoKQpgYGAKCiMgUHJlcGFyYXRpb24KCkluIHRoaXMgc2VjdGlvbiwgd2Ugc2V0IHRoZSBnbG9iYWwgc2V0dGluZ3Mgb2YgdGhlIGFuYWx5c2lzLiBXZSB3aWxsIHN0b3JlIGRhdGEgdGhlcmUgOgoKYGBge3Igb3V0X2Rpcn0Kc2F2ZV9uYW1lID0gImhmc2MiCm91dF9kaXIgPSAiLiIKYGBgCgpXZSBsb2FkIHRoZSBkYXRhc2V0IDoKCmBgYHtyIGxvYWRfc29ian0Kc29iaiA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvIiwgc2F2ZV9uYW1lLCAiX3NvYmoucmRzIikpCnNvYmoKYGBgCgpXZSBsb2FkIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24gOgoKYGBge3IgY3VzdG9tX3BhbGV0dGVfc2FtcGxlLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNn0Kc2FtcGxlX2luZm8gPSByZWFkUkRTKHBhc3RlMChvdXRfZGlyLCAiLy4uLy4uLzFfbWV0YWRhdGEvaHNfaGRfc2FtcGxlX2luZm8ucmRzIikpCnByb2plY3RfbmFtZXNfb2kgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUKCmdyYXBoaWNzOjpwaWUocmVwKDEsIG5yb3coc2FtcGxlX2luZm8pKSwKICAgICAgICAgICAgICBjb2wgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICBsYWJlbHMgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpCmBgYAoKSGVyZSBhcmUgY3VzdG9tIGNvbG9ycyBmb3IgZWFjaCBjZWxsIHR5cGUgOgoKYGBge3IgY29sb3JfbWFya2VycywgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxLjIsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjb2xvcl9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAob3V0X2RpciwgIi8uLi8uLi8xX21ldGFkYXRhL2hzX2hkX2NvbG9yX21hcmtlcnMucmRzIikpCgpkYXRhLmZyYW1lKGNlbGxfdHlwZSA9IG5hbWVzKGNvbG9yX21hcmtlcnMpLAogICAgICAgICAgIGNvbG9yID0gdW5saXN0KGNvbG9yX21hcmtlcnMpKSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoLiwgYWVzKHggPSBjZWxsX3R5cGUsIHkgPSAwLCBmaWxsID0gY2VsbF90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQocGNoID0gMjEsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KGNvbG9yX21hcmtlcnMpLCBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSkKYGBgCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG9mIGludGVyZXN0IDoKCmBgYHtyIG5hbWUyRH0KbmFtZTJEID0gImhhcm1vbnlfMjRfdHNuZSIKYGBgCgpXZSBkZXNpZ24gYSBjdXN0b20gZnVuY3Rpb24gdG8gbWFrZSB0aGUgR1NFQSBwbG90IGFuZCBhIHdvcmQgY2xvdWQgZ3JhcGggOgoKYGBge3IgbWFrZV9nc2VhX3Bsb3QsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQptYWtlX2dzZWFfcGxvdCA9IGZ1bmN0aW9uKGdzZWFfcmVzdWx0cywgZ3Nfb2ksIGZvbGRfY2hhbmdlLCBtZXRyaWMgPSAiRkMiKSB7CiAgZm9sZF9jaGFuZ2UkbWV0cmljID0gZm9sZF9jaGFuZ2VbLCBtZXRyaWNdCiAgCiAgcGxvdF9saXN0ID0gbGFwcGx5KGdzX29pLCBGVU4gPSBmdW5jdGlvbihnZW5lX3NldCkgewogICAgIyBHZW5lIHNldCBjb250ZW50CiAgICBnc19jb250ZW50ID0gZ2VuZV9zZXRzICU+JQogICAgICBkcGx5cjo6ZmlsdGVyKGdzX25hbWUgPT0gZ2VuZV9zZXQpICU+JQogICAgICBkcGx5cjo6cHVsbChlbnNlbWJsX2dlbmUpICU+JQogICAgICB1bmlxdWUoKQogICAgCiAgICAjIEdlbmUgc2V0IHNpemUKICAgIG5iX2dlbmVzID0gbGVuZ3RoKGdzX2NvbnRlbnQpCiAgICAKICAgICMgRW5yaWNobWVudCBtZXRyaWNzCiAgICBORVMgPSBnc2VhX3Jlc3VsdHNAcmVzdWx0W2dlbmVfc2V0LCAiTkVTIl0KICAgIHAuYWRqdXN0ID0gZ3NlYV9yZXN1bHRzQHJlc3VsdFtnZW5lX3NldCwgInAuYWRqdXN0Il0gJT4lCiAgICAgIHJvdW5kKC4sIDQpCiAgICBxdmFsdWVzID0gZ3NlYV9yZXN1bHRzQHJlc3VsdFtnZW5lX3NldCwgInF2YWx1ZXMiXQogICAgCiAgICBpZiAocC5hZGp1c3QgPiAwLjA1KSB7CiAgICAgIHAuYWRqdXN0ID0gcGFzdGUwKCI8c3BhbiBzdHlsZT0nY29sb3I6cmVkOyc+IiwgcC5hZGp1c3QsICI8L3NwYW4+IikKICAgIH0KICAgIAogICAgbXlfc3VidGl0bGUgPSBwYXN0ZTAoIlxuTkVTIDogIiwgcm91bmQoTkVTLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICIgfCBwYWRqIDogIiwgcC5hZGp1c3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAiIHwgcXZhbCA6ICIsIHJvdW5kKHF2YWx1ZXMsIDQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIiB8IHNldCBzaXplIDogIiwgbmJfZ2VuZXMsICIgZ2VuZXMiKQogICAgCiAgICAjIFNpemUgbGltaXRzCiAgICBsb3dlcl9GQyA9IG1pbihmb2xkX2NoYW5nZVtnc19jb250ZW50LCBdJG1ldHJpYywgbmEucm0gPSBUUlVFKQogICAgdXBwZXJfRkMgPSBtYXgoZm9sZF9jaGFuZ2VbZ3NfY29udGVudCwgXSRtZXRyaWMsIG5hLnJtID0gVFJVRSkKICAgIAogICAgIyBQbG90CiAgICBwID0gZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBnc2VhX3Jlc3VsdHMsIGdlbmVTZXRJRCA9IGdlbmVfc2V0KSArCiAgICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lX3NldCwKICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IG15X3N1YnRpdGxlKSArCiAgICAgIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDMsIDMsIDUsIDMpKSwKICAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGdndGV4dDo6ZWxlbWVudF9tYXJrZG93bihoanVzdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQogICAgCiAgICB3YyA9IGdncGxvdDI6OmdncGxvdChmb2xkX2NoYW5nZVtnc19jb250ZW50LCBdLAogICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gZ2VuZV9uYW1lLCBzaXplID0gYWJzKG1ldHJpYyksIGNvbG9yID0gbWV0cmljKSkgKwogICAgICBnZ3dvcmRjbG91ZDo6Z2VvbV90ZXh0X3dvcmRjbG91ZF9hcmVhKHNob3cubGVnZW5kID0gVFJVRSkgKwogICAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudDIoCiAgICAgICAgbmFtZSA9IG1ldHJpYywKICAgICAgICBsb3cgPSBhcXVhcml1czo6Y29sb3JfY252WzFdLAogICAgICAgIG1pZCA9ICJncmF5NzAiLCBtaWRwb2ludCA9IDAsCiAgICAgICAgaGlnaCA9IGFxdWFyaXVzOjpjb2xvcl9jbnZbM10pICsKICAgICAgZ2dwbG90Mjo6c2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNykgKwogICAgICBnZ3Bsb3QyOjp0aGVtZV9taW5pbWFsKCkgKwogICAgICBnZ3Bsb3QyOjpndWlkZXMoc2l6ZSA9ICJub25lIikKICAgIAogICAgcmV0dXJuKGxpc3QocCwgd2MpKQogIH0pICU+JSB1bmxpc3QoLiwgcmVjdXJzaXZlID0gRkFMU0UpCiAgCiAgcmV0dXJuKHBsb3RfbGlzdCkKfQpgYGAKCiMgVmlzdWFsaXphdGlvbgoKIyMgR2VuZSBleHByZXNzaW9uCgpXZSB2aXN1YWxpemUgZ2VuZSBleHByZXNzaW9uIGZvciBzb21lIG1hcmtlcnMgOgoKYGBge3IgcGxvdF9saXN0X2ZlYXR1cmVzLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CmZlYXR1cmVzID0gYygicGVyY2VudC5tdCIsICJwZXJjZW50LnJiIiwgIm5GZWF0dXJlX1JOQSIpCgpwbG90X2xpc3QgPSBsYXBwbHkoZmVhdHVyZXMsIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJEKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAzKQpgYGAKCiMjIENsdXN0ZXJzCgpXZSB2aXN1YWxpemUgY2x1c3RlcnMgOgoKYGBge3Igc2VlX2NsdXN0ZXJpbmcsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpjbHVzdGVyX3Bsb3QgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBsYWJlbCA9IFRSVUUpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpjbHVzdGVyX3Bsb3QKYGBgCgpXZSB2aXN1YWxpemUgY2x1c3RlciBzcGxpdCBieSBzYW1wbGUgOgoKYGBge3IgcGxvdF9zcGxpdF9kaW1yZWQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gN30KcGxvdF9saXN0ID0gYXF1YXJpdXM6OnBsb3Rfc3BsaXRfZGltcmVkKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdF9ieSA9ICJwcm9qZWN0X25hbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0X2NvbG9yID0gc2V0TmFtZXMoc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5tID0gc2FtcGxlX2luZm8kcHJvamVjdF9uYW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJnX3B0X3NpemUgPSAwLjUsIG1haW5fcHRfc2l6ZSA9IDAuNSkKCnBsb3RfbGlzdFtbbGVuZ3RoKHBsb3RfbGlzdCkgKyAxXV0gPSBjbHVzdGVyX3Bsb3QKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSA0KSAmCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKIyBBbmFseXNpcwoKV2UgbWFrZSBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gZWFjaCBjbHVzdGVyIGFuZCBhbGwgb3RoZXJzLiBUaGVuLCB3ZSBjb21wdXRlIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIEhTIGFuZCBIRCwgYWxsIGNlbGxzIGNvbnNpZGVyZWQuCgpXZSBzYXZlIHRoZSByZXN1bHRzIGluIGEgbGlzdCA6CgpgYGB7ciBsaXN0X3Jlc3VsdHN9Cmxpc3RfcmVzdWx0cyA9IGxpc3QoKQpgYGAKCkZvciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBIUyBhbmQgSEQsIHdlIG1ha2UgR1NFQSB1c2luZyBnZW5lIHNldHMgZnJvbSBNU2lnREIgOgoKYGBge3IgZ2VuZV9zZXRzfQpnZW5lX3NldHMgPSBhcXVhcml1czo6Z2V0X2dlbmVfc2V0cyhzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIpCmdlbmVfc2V0cyA9IGdlbmVfc2V0cyRnZW5lX3NldHMKCmhlYWQoZ2VuZV9zZXRzKQpgYGAKCkhvdyBtYW55IGdlbmUgc2V0cyA/CgpgYGB7ciBjb3VudF9nZW5lX3NldHN9CmdlbmVfc2V0c1ssIGMoImdzX3N1YmNhdCIsICJnc19uYW1lIildICU+JQogIGRwbHlyOjpkaXN0aW5jdCgpICU+JQogIGRwbHlyOjpwdWxsKGdzX3N1YmNhdCkgJT4lCiAgdGFibGUoKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkNhdGVnb3J5IiwgIk5iIGdlbmUgc2V0cyIpKQpgYGAKCldlIGdldCBnZW5lIG5hbWUgYW5kIGdlbmUgSUQgY29ycmVzcG9uZGVuY2UgOgoKYGBge3IgZ2VuZV9jb3JyZXNwfQpnZW5lX2NvcnJlc3AgPSBzb2JqQGFzc2F5c1tbIlJOQSJdXUBtZXRhLmZlYXR1cmVzWywgYygiZ2VuZV9uYW1lIiwgIkVuc2VtYmxfSUQiKV0gJT4lCiAgYGNvbG5hbWVzPC1gKGMoIk5BTUUiLCAiSUQiKSkgJT4lCiAgZHBseXI6Om11dGF0ZShJRCA9IGFzLmNoYXJhY3RlcihJRCkpCnJvd25hbWVzKGdlbmVfY29ycmVzcCkgPSBnZW5lX2NvcnJlc3AkSUQKCmhlYWQoZ2VuZV9jb3JyZXNwKQpgYGAKCiMjIENsdXN0ZXJzIGlkZW50aXR5CgojIyMgQ2x1c3RlciAwCgpXZSBzZXQgdGhlIGNsdXN0ZXIgb2YgaW50ZXJlc3QgOgoKYGBge3IgY2x1c3RlcnNfb2lfY2x1c3QwfQpjbHVzdGVyc19vaSA9IDAKdGFibGUoc29iaiRzZXVyYXRfY2x1c3RlcnMsIHNvYmokc2FtcGxlX3R5cGUpW2NsdXN0ZXJzX29pICsgMSwgXQpgYGAKCldlIHZpc3VhbGl6ZSB0aGVzZSBjZWxscyBvbiB0aGUgcHJvamVjdGlvbiA6CgpgYGB7ciBzZWVfY2x1c3QwLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KYXF1YXJpdXM6OnBsb3RfcmVkX2FuZF9ibHVlKHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMSA9IGNsdXN0ZXJzX29pKQpgYGAKCldoYXQgaXMgdGhlIGNsdXN0ZXIgaWRlbnRpdHkgPwoKYGBge3IgaWRlbnRfY2x1c3QwLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0KbWFyayA9IFNldXJhdDo6RmluZE1hcmtlcnMoc29iaiwgaWRlbnQuMSA9IGNsdXN0ZXJzX29pKQoKbWFyayA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA+IDApICU+JQogIGRwbHlyOjpmaWx0ZXIoYWJzKHBjdC4xIC0gcGN0LjIpID4gMC4yKSAlPiUKICBkcGx5cjo6YXJyYW5nZSgtKHBjdC4xIC0gcGN0LjIpLCAtYXZnX2xvZ0ZDKQoKbGlzdF9yZXN1bHRzW1twYXN0ZTAoImNsdXN0ZXJfIiwgY2x1c3RlcnNfb2ksICJfdnNfYWxsIildXSA9IG1hcmsKCmRpbShtYXJrKQpoZWFkKG1hcmssIG4gPSAyMCkKYGBgCgpXZSB2aXN1YWxpemUgZXhwcmVzc2lvbiBmb3IgdG9wIDkgZ2VuZXMgOgoKYGBge3Igc2VlX2NsdXN0MF90b3A5LCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQpwbG90X2xpc3QgPSBsYXBwbHkocm93bmFtZXMobWFyaylbYygxOjkpXSAlPiUgbmEub21pdCgpLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIFNldXJhdDo6RmVhdHVyZVBsb3Qoc29iaiwgZmVhdHVyZXMgPSBvbmVfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gMykKYGBgCgojIyMgQ2x1c3RlciAxCgpXZSBzZXQgdGhlIGNsdXN0ZXIgb2YgaW50ZXJlc3QgOgoKYGBge3IgY2x1c3RlcnNfb2lfY2x1c3QxfQpjbHVzdGVyc19vaSA9IDEKdGFibGUoc29iaiRzZXVyYXRfY2x1c3RlcnMsIHNvYmokc2FtcGxlX3R5cGUpW2NsdXN0ZXJzX29pICsgMSwgXQpgYGAKCldlIHZpc3VhbGl6ZSB0aGVzZSBjZWxscyBvbiB0aGUgcHJvamVjdGlvbiA6CgpgYGB7ciBzZWVfY2x1c3QxLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KYXF1YXJpdXM6OnBsb3RfcmVkX2FuZF9ibHVlKHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMSA9IGNsdXN0ZXJzX29pKQpgYGAKCldoYXQgaXMgdGhlIGNsdXN0ZXIgaWRlbnRpdHkgPwoKYGBge3IgaWRlbnRfY2x1c3QxLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0KbWFyayA9IFNldXJhdDo6RmluZE1hcmtlcnMoc29iaiwgaWRlbnQuMSA9IGNsdXN0ZXJzX29pKQoKbWFyayA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA+IDApICU+JQogIGRwbHlyOjphcnJhbmdlKC0ocGN0LjEgLSBwY3QuMiksIC1hdmdfbG9nRkMpCgpsaXN0X3Jlc3VsdHNbW3Bhc3RlMCgiY2x1c3Rlcl8iLCBjbHVzdGVyc19vaSwgIl92c19hbGwiKV1dID0gbWFyawoKZGltKG1hcmspCmhlYWQobWFyaywgbiA9IDIwKQpgYGAKCldlIHZpc3VhbGl6ZSBleHByZXNzaW9uIGZvciB0b3AgOSBnZW5lcyA6CgpgYGB7ciBzZWVfY2x1c3QxX3RvcDksIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CnBsb3RfbGlzdCA9IGxhcHBseShyb3duYW1lcyhtYXJrKVtjKDE6OSldICU+JSBuYS5vbWl0KCksIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJEKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAzKQpgYGAKCgojIyMgQ2x1c3RlciAyCgpXZSBzZXQgdGhlIGNsdXN0ZXIgb2YgaW50ZXJlc3QgOgoKYGBge3IgY2x1c3RlcnNfb2lfY2x1c3QyfQpjbHVzdGVyc19vaSA9IDIKdGFibGUoc29iaiRzZXVyYXRfY2x1c3RlcnMsIHNvYmokc2FtcGxlX3R5cGUpW2NsdXN0ZXJzX29pICsgMSwgXQpgYGAKCldlIHZpc3VhbGl6ZSB0aGVzZSBjZWxscyBvbiB0aGUgcHJvamVjdGlvbiA6CgpgYGB7ciBzZWVfY2x1c3QyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KYXF1YXJpdXM6OnBsb3RfcmVkX2FuZF9ibHVlKHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMSA9IGNsdXN0ZXJzX29pKQpgYGAKCldoYXQgaXMgdGhlIGNsdXN0ZXIgaWRlbnRpdHkgPwoKYGBge3IgaWRlbnRfY2x1c3QyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0KbWFyayA9IFNldXJhdDo6RmluZE1hcmtlcnMoc29iaiwgaWRlbnQuMSA9IGNsdXN0ZXJzX29pKQoKbWFyayA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA+IDApICU+JQogIGRwbHlyOjphcnJhbmdlKC0ocGN0LjEgLSBwY3QuMiksIC1hdmdfbG9nRkMpCgpsaXN0X3Jlc3VsdHNbW3Bhc3RlMCgiY2x1c3Rlcl8iLCBjbHVzdGVyc19vaSwgIl92c19hbGwiKV1dID0gbWFyawoKZGltKG1hcmspCmhlYWQobWFyaywgbiA9IDIwKQpgYGAKCldlIHZpc3VhbGl6ZSBleHByZXNzaW9uIGZvciB0b3AgOSBnZW5lcyA6CgpgYGB7ciBzZWVfY2x1c3QyX3RvcDksIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CnBsb3RfbGlzdCA9IGxhcHBseShyb3duYW1lcyhtYXJrKVtjKDE6OSldICU+JSBuYS5vbWl0KCksIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJEKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAzKQpgYGAKCiMjIyBDbHVzdGVyIDMKCldlIHNldCB0aGUgY2x1c3RlciBvZiBpbnRlcmVzdCA6CgpgYGB7ciBjbHVzdGVyc19vaV9jbHVzdDN9CmNsdXN0ZXJzX29pID0gMwp0YWJsZShzb2JqJHNldXJhdF9jbHVzdGVycywgc29iaiRzYW1wbGVfdHlwZSlbY2x1c3RlcnNfb2kgKyAxLCBdCmBgYAoKV2UgdmlzdWFsaXplIHRoZXNlIGNlbGxzIG9uIHRoZSBwcm9qZWN0aW9uIDoKCmBgYHtyIHNlZV9jbHVzdDMsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA3fQphcXVhcml1czo6cGxvdF9yZWRfYW5kX2JsdWUoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAxID0gY2x1c3RlcnNfb2kpCmBgYAoKV2hhdCBpcyB0aGUgY2x1c3RlciBpZGVudGl0eSA/CgpgYGB7ciBpZGVudF9jbHVzdDMsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQptYXJrID0gU2V1cmF0OjpGaW5kTWFya2Vycyhzb2JqLCBpZGVudC4xID0gY2x1c3RlcnNfb2kpCgptYXJrID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUpICU+JQogIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZ0ZDID4gMCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLShwY3QuMSAtIHBjdC4yKSwgLWF2Z19sb2dGQykKCmxpc3RfcmVzdWx0c1tbcGFzdGUwKCJjbHVzdGVyXyIsIGNsdXN0ZXJzX29pLCAiX3ZzX2FsbCIpXV0gPSBtYXJrCgpkaW0obWFyaykKaGVhZChtYXJrLCBuID0gMjApCmBgYAoKV2UgdmlzdWFsaXplIGV4cHJlc3Npb24gZm9yIHRvcCA5IGdlbmVzIDoKCmBgYHtyIHNlZV9jbHVzdDNfdG9wOSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KcGxvdF9saXN0ID0gbGFwcGx5KHJvd25hbWVzKG1hcmspW2MoMTo5KV0gJT4lIG5hLm9taXQoKSwgRlVOID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIGZlYXR1cmVzID0gb25lX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogICAgU2V1cmF0OjpOb0F4ZXMoKQp9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDMpCmBgYAoKIyMjIENsdXN0ZXIgNAoKV2Ugc2V0IHRoZSBjbHVzdGVyIG9mIGludGVyZXN0IDoKCmBgYHtyIGNsdXN0ZXJzX29pX2NsdXN0NH0KY2x1c3RlcnNfb2kgPSA0CnRhYmxlKHNvYmokc2V1cmF0X2NsdXN0ZXJzLCBzb2JqJHNhbXBsZV90eXBlKVtjbHVzdGVyc19vaSArIDEsIF0KYGBgCgpXZSB2aXN1YWxpemUgdGhlc2UgY2VsbHMgb24gdGhlIHByb2plY3Rpb24gOgoKYGBge3Igc2VlX2NsdXN0NCwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDd9CmFxdWFyaXVzOjpwbG90X3JlZF9hbmRfYmx1ZShzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEgPSBjbHVzdGVyc19vaSkKYGBgCgpXaGF0IGlzIHRoZSBjbHVzdGVyIGlkZW50aXR5ID8KCmBgYHtyIGlkZW50X2NsdXN0NCwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDV9Cm1hcmsgPSBTZXVyYXQ6OkZpbmRNYXJrZXJzKHNvYmosIGlkZW50LjEgPSBjbHVzdGVyc19vaSkKCm1hcmsgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lCiAgZHBseXI6OmZpbHRlcihhdmdfbG9nRkMgPiAwKSAlPiUKICBkcGx5cjo6YXJyYW5nZSgtKHBjdC4xIC0gcGN0LjIpLCAtYXZnX2xvZ0ZDKQoKbGlzdF9yZXN1bHRzW1twYXN0ZTAoImNsdXN0ZXJfIiwgY2x1c3RlcnNfb2ksICJfdnNfYWxsIildXSA9IG1hcmsKCmRpbShtYXJrKQpoZWFkKG1hcmssIG4gPSAyMCkKYGBgCgpXZSB2aXN1YWxpemUgZXhwcmVzc2lvbiBmb3IgdG9wIDkgZ2VuZXMgOgoKYGBge3Igc2VlX2NsdXN0NF90b3A5LCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQpwbG90X2xpc3QgPSBsYXBwbHkocm93bmFtZXMobWFyaylbYygxOjkpXSAlPiUgbmEub21pdCgpLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIFNldXJhdDo6RmVhdHVyZVBsb3Qoc29iaiwgZmVhdHVyZXMgPSBvbmVfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gMykKYGBgCgojIyMgQ2x1c3RlciA1CgpXZSBzZXQgdGhlIGNsdXN0ZXIgb2YgaW50ZXJlc3QgOgoKYGBge3IgY2x1c3RlcnNfb2lfY2x1c3Q1fQpjbHVzdGVyc19vaSA9IDUKdGFibGUoc29iaiRzZXVyYXRfY2x1c3RlcnMsIHNvYmokc2FtcGxlX3R5cGUpW2NsdXN0ZXJzX29pICsgMSwgXQpgYGAKCldlIHZpc3VhbGl6ZSB0aGVzZSBjZWxscyBvbiB0aGUgcHJvamVjdGlvbiA6CgpgYGB7ciBzZWVfY2x1c3Q1LCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KYXF1YXJpdXM6OnBsb3RfcmVkX2FuZF9ibHVlKHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMSA9IGNsdXN0ZXJzX29pKQpgYGAKCldoYXQgaXMgdGhlIGNsdXN0ZXIgaWRlbnRpdHkgPwoKYGBge3IgaWRlbnRfY2x1c3Q1LCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0KbWFyayA9IFNldXJhdDo6RmluZE1hcmtlcnMoc29iaiwgaWRlbnQuMSA9IGNsdXN0ZXJzX29pKQoKbWFyayA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA+IDApICU+JQogIGRwbHlyOjphcnJhbmdlKC0ocGN0LjEgLSBwY3QuMiksIC1hdmdfbG9nRkMpCgpsaXN0X3Jlc3VsdHNbW3Bhc3RlMCgiY2x1c3Rlcl8iLCBjbHVzdGVyc19vaSwgIl92c19hbGwiKV1dID0gbWFyawoKZGltKG1hcmspCmhlYWQobWFyaywgbiA9IDIwKQpgYGAKCldlIHZpc3VhbGl6ZSBleHByZXNzaW9uIGZvciB0b3AgOSBnZW5lcyA6CgpgYGB7ciBzZWVfY2x1c3Q1X3RvcDksIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CnBsb3RfbGlzdCA9IGxhcHBseShyb3duYW1lcyhtYXJrKVtjKDE6OSldICU+JSBuYS5vbWl0KCksIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJEKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAzKQpgYGAKCiMjIyBDbHVzdGVyIDYKCldlIHNldCB0aGUgY2x1c3RlciBvZiBpbnRlcmVzdCA6CgpgYGB7ciBjbHVzdGVyc19vaV9jbHVzdDZ9CmNsdXN0ZXJzX29pID0gNgp0YWJsZShzb2JqJHNldXJhdF9jbHVzdGVycywgc29iaiRzYW1wbGVfdHlwZSlbY2x1c3RlcnNfb2kgKyAxLCBdCmBgYAoKV2UgdmlzdWFsaXplIHRoZXNlIGNlbGxzIG9uIHRoZSBwcm9qZWN0aW9uIDoKCmBgYHtyIHNlZV9jbHVzdDYsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA3fQphcXVhcml1czo6cGxvdF9yZWRfYW5kX2JsdWUoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAxID0gY2x1c3RlcnNfb2kpCmBgYAoKV2hhdCBpcyB0aGUgY2x1c3RlciBpZGVudGl0eSA/CgpgYGB7ciBpZGVudF9jbHVzdDYsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQptYXJrID0gU2V1cmF0OjpGaW5kTWFya2Vycyhzb2JqLCBpZGVudC4xID0gY2x1c3RlcnNfb2kpCgptYXJrID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUpICU+JQogIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZ0ZDID4gMCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLShwY3QuMSAtIHBjdC4yKSwgLWF2Z19sb2dGQykKCmxpc3RfcmVzdWx0c1tbcGFzdGUwKCJjbHVzdGVyXyIsIGNsdXN0ZXJzX29pLCAiX3ZzX2FsbCIpXV0gPSBtYXJrCgpkaW0obWFyaykKaGVhZChtYXJrLCBuID0gMjApCmBgYAoKV2UgdmlzdWFsaXplIGV4cHJlc3Npb24gZm9yIHRvcCA5IGdlbmVzIDoKCmBgYHtyIHNlZV9jbHVzdDZfdG9wOSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KcGxvdF9saXN0ID0gbGFwcGx5KHJvd25hbWVzKG1hcmspW2MoMTo5KV0gJT4lIG5hLm9taXQoKSwgRlVOID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIGZlYXR1cmVzID0gb25lX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogICAgU2V1cmF0OjpOb0F4ZXMoKQp9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDMpCmBgYAoKIyMjIENsdXN0ZXIgNwoKV2Ugc2V0IHRoZSBjbHVzdGVyIG9mIGludGVyZXN0IDoKCmBgYHtyIGNsdXN0ZXJzX29pX2NsdXN0N30KY2x1c3RlcnNfb2kgPSA3CnRhYmxlKHNvYmokc2V1cmF0X2NsdXN0ZXJzLCBzb2JqJHNhbXBsZV90eXBlKVtjbHVzdGVyc19vaSArIDEsIF0KYGBgCgpXZSB2aXN1YWxpemUgdGhlc2UgY2VsbHMgb24gdGhlIHByb2plY3Rpb24gOgoKYGBge3Igc2VlX2NsdXN0NywgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDd9CmFxdWFyaXVzOjpwbG90X3JlZF9hbmRfYmx1ZShzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEgPSBjbHVzdGVyc19vaSkKYGBgCgpXaGF0IGlzIHRoZSBjbHVzdGVyIGlkZW50aXR5ID8KCmBgYHtyIGlkZW50X2NsdXN0NywgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDV9Cm1hcmsgPSBTZXVyYXQ6OkZpbmRNYXJrZXJzKHNvYmosIGlkZW50LjEgPSBjbHVzdGVyc19vaSkKCm1hcmsgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lCiAgZHBseXI6OmZpbHRlcihhdmdfbG9nRkMgPiAwKSAlPiUKICBkcGx5cjo6YXJyYW5nZSgtKHBjdC4xIC0gcGN0LjIpLCAtYXZnX2xvZ0ZDKQoKbGlzdF9yZXN1bHRzW1twYXN0ZTAoImNsdXN0ZXJfIiwgY2x1c3RlcnNfb2ksICJfdnNfYWxsIildXSA9IG1hcmsKCmRpbShtYXJrKQpoZWFkKG1hcmssIG4gPSAyMCkKYGBgCgpXZSB2aXN1YWxpemUgZXhwcmVzc2lvbiBmb3IgdG9wIDkgZ2VuZXMgOgoKYGBge3Igc2VlX2NsdXN0N190b3A5LCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQpwbG90X2xpc3QgPSBsYXBwbHkocm93bmFtZXMobWFyaylbYygxOjkpXSAlPiUgbmEub21pdCgpLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIFNldXJhdDo6RmVhdHVyZVBsb3Qoc29iaiwgZmVhdHVyZXMgPSBvbmVfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gMykKYGBgCgojIyMgQ2x1c3RlciA4CgpXZSBzZXQgdGhlIGNsdXN0ZXIgb2YgaW50ZXJlc3QgOgoKYGBge3IgY2x1c3RlcnNfb2lfY2x1c3Q4fQpjbHVzdGVyc19vaSA9IDgKdGFibGUoc29iaiRzZXVyYXRfY2x1c3RlcnMsIHNvYmokc2FtcGxlX3R5cGUpW2NsdXN0ZXJzX29pICsgMSwgXQpgYGAKCldlIHZpc3VhbGl6ZSB0aGVzZSBjZWxscyBvbiB0aGUgcHJvamVjdGlvbiA6CgpgYGB7ciBzZWVfY2x1c3Q4LCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KYXF1YXJpdXM6OnBsb3RfcmVkX2FuZF9ibHVlKHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMSA9IGNsdXN0ZXJzX29pKQpgYGAKCldoYXQgaXMgdGhlIGNsdXN0ZXIgaWRlbnRpdHkgPwoKYGBge3IgaWRlbnRfY2x1c3Q4LCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0KbWFyayA9IFNldXJhdDo6RmluZE1hcmtlcnMoc29iaiwgaWRlbnQuMSA9IGNsdXN0ZXJzX29pKQoKbWFyayA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA+IDApICU+JQogIGRwbHlyOjphcnJhbmdlKC0ocGN0LjEgLSBwY3QuMiksIC1hdmdfbG9nRkMpCgpsaXN0X3Jlc3VsdHNbW3Bhc3RlMCgiY2x1c3Rlcl8iLCBjbHVzdGVyc19vaSwgIl92c19hbGwiKV1dID0gbWFyawoKZGltKG1hcmspCmhlYWQobWFyaywgbiA9IDIwKQpgYGAKCldlIHZpc3VhbGl6ZSBleHByZXNzaW9uIGZvciB0b3AgOSBnZW5lcyA6CgpgYGB7ciBzZWVfY2x1c3Q4X3RvcDksIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CnBsb3RfbGlzdCA9IGxhcHBseShyb3duYW1lcyhtYXJrKVtjKDE6OSldICU+JSBuYS5vbWl0KCksIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJEKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAzKQpgYGAKCgojIyBIUyB2cyBIRAoKV2UgY2hhbmdlIHRoZSBjZWxscyBpZGVudGl0eSA6CgpgYGB7ciBjaGFuZ2VfaWRlbnR9ClNldXJhdDo6SWRlbnRzKHNvYmopID0gc29iaiRzYW1wbGVfdHlwZQp0YWJsZShTZXVyYXQ6OklkZW50cyhzb2JqKSkKYGBgCgpXZSBjb21wYXJlIGFsbCBjZWxscyBiZXR3ZWVuIEhTIGFuZCBIRCA6CgpgYGB7ciBzZWVfaHNfaGQsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA3fQphcXVhcml1czo6cGxvdF9yZWRfYW5kX2JsdWUoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAxID0gIkhTIiwgZ3JvdXAyID0gIkhEIikKYGBgCgojIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24KCldlIG1ha2UgYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBiZXR3ZWVuIGNlbGxzIGZyb20gSFMgYW5kIGNlbGxzIGZyb20gSEQgOgoKYGBge3IgaHNfdnNfaGRfZGUsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQptYXJrID0gU2V1cmF0OjpGaW5kTWFya2Vycyhzb2JqLCBpZGVudC4xID0gIkhTIiwgaWRlbnQuMiA9ICJIRCIpCgptYXJrID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUpCgpsaXN0X3Jlc3VsdHNbWyJIU192c19IRCJdXSA9IG1hcmsKYGBgCgpIb3cgbWFueSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYnkgY29uZGl0aW9uID8KCmBgYHtyIG5iX2dlbmVzfQojIEVucmljaGVkIGluIEhTCm1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihhdmdfbG9nRkMgPiAwKSAlPiUKICBucm93KCkKCiMgRW5yaWNoZWQgaW4gSEQKbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA8IDApICU+JQogIG5yb3coKQpgYGAKClRoZXJlIGFyZSBubyBzaWduaWZpY2FudGx5IGVucmljaGVkIGdlbmVzIGluIEhEIGNvbXBhcmVkIHRvIEhTLiBXaGF0IGFyZSB0aGUgZ2VuZXMgdXAtcmVndWxhdGVkIGluIEhTID8KCmBgYHtyIGVucmljaGVkX2hzfQpnZW5lcyA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihhdmdfbG9nRkMgPiAwKSAlPiUKICByb3duYW1lcyAlPiUKICBzb3J0KCkKCmdlbmVzCmBgYAoKIyMjIEhlYXRtYXAKCldlIHZpc3VhbGl6ZSB0aGUgZXhwcmVzc2lvbiBvZiB0aGVzZSBnZW5lcyBvbiBhIGhlYXRtYXAuCgpgYGB7ciBwcmVwX2dlbmVzfQpmZWF0dXJlc19vaSA9IGdlbmVzCgpsZW5ndGgoZmVhdHVyZXNfb2kpCmBgYAoKV2UgcHJlcGFyZSB0aGUgc2NhbGVkIGV4cHJlc3Npb24gbWF0cml4IDoKCmBgYHtyIG1hdH0KbWF0X2V4cHJlc3Npb24gPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzb2JqLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImRhdGEiKVtmZWF0dXJlc19vaSwgXQptYXRfZXhwcmVzc2lvbiA9IE1hdHJpeDo6dChtYXRfZXhwcmVzc2lvbikKbWF0X2V4cHJlc3Npb24gPSBkeW51dGlsczo6c2NhbGVfcXVhbnRpbGUobWF0X2V4cHJlc3Npb24pICMgYmV0d2VlbiAwIGFuZCAxCm1hdF9leHByZXNzaW9uID0gTWF0cml4Ojp0KG1hdF9leHByZXNzaW9uKQptYXRfZXhwcmVzc2lvbiA9IGFzLm1hdHJpeChtYXRfZXhwcmVzc2lvbikgIyBub3Qgc3BhcnNlCgpkaW0obWF0X2V4cHJlc3Npb24pCmBgYAoKV2UgcHJlcGFyZSB0aGUgaGVhdG1hcCBhbm5vdGF0aW9uIDoKCmBgYHtyIGhhX3RvcH0KaGFfdG9wID0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIHNhbXBsZV90eXBlID0gc29iaiRzYW1wbGVfdHlwZSwKICBjbHVzdGVyID0gc29iaiRzZXVyYXRfY2x1c3RlcnMsCiAgY29sID0gbGlzdChzYW1wbGVfdHlwZSA9IHNldE5hbWVzKG5tID0gYygiSFMiLCAiSEQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiI0M1NUY0MCIsICIjMkM3OEU2IikpLAogICAgICAgICAgICAgY2x1c3RlciA9IHNldE5hbWVzKG5tID0gbGV2ZWxzKHNvYmokc2V1cmF0X2NsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcXVhcml1czo6Z2dfY29sb3JfaHVlKGxlbmd0aChsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpKSkpKSkKYGBgCgpBbmQgdGhlIGhlYXRtYXAgOgoKYGBge3IgaGVhdG1hcCwgZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA0MH0Kc29iaiRjZWxsX2dyb3VwID0gcGFzdGUwKHNvYmokc2FtcGxlX3R5cGUsICJfIiwgc29iaiRzZXVyYXRfY2x1c3RlcnMpICU+JQogIGFzLmZhY3RvcigpCgpodCA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKG1hdF9leHByZXNzaW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGFxdWFyaXVzOjpjb2xvcl9jbnYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBbm5vdGF0aW9uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoYV90b3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBHcm91cGluZwogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9vcmRlciA9IHNvYmpAbWV0YS5kYXRhICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OmFycmFuZ2Uoc2FtcGxlX3R5cGUsIHNldXJhdF9jbHVzdGVycykgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcygpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9zcGxpdCA9IHNvYmokY2VsbF9ncm91cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fZ2FwID0gdW5pdC5jKHJlcCh1bml0KDAuMDEsICJtbSIpLCA4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXQoMiwgIm1tIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAodW5pdCgwLjAxLCAibW0iKSwgOCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90aXRsZSA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVmlzdWFsIGFzcGVjdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlciA9IFRSVUUpCgpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwKICAgICAgICAgICAgICAgICAgICAgbWVyZ2VfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIpCmBgYAoKIyMjIEdTRUEKCldlIHJ1biBhIEdTRUEgZm9yIGFsbCBnZW5lIHNldHMsIGZyb20gdGhlIGZ1bGwgY291bnQgbWF0cml4IDoKCmBgYHtyIGdzZWFfaHNfdnNfaGQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNDB9CnJhbmtlZF9nZW5lX2xpc3QgPSBhcXVhcml1czo6cnVuX2ZvbGRjaGFuZ2UoU2V1cmF0OjpHZXRBc3NheURhdGEoc29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJjb3VudHMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEgPSBjb2xuYW1lcyhzb2JqKVtzb2JqQGFjdGl2ZS5pZGVudCAlaW4lICJIUyJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMiA9IGNvbG5hbWVzKHNvYmopW3NvYmpAYWN0aXZlLmlkZW50ICVpbiUgIkhEIl0pCm5hbWVzKHJhbmtlZF9nZW5lX2xpc3QpID0gZ2VuZV9jb3JyZXNwJElECgpnc2VhX3Jlc3VsdHMgPSBhcXVhcml1czo6Z3NlYV9ydW4ocmFua2VkX2dlbmVfbGlzdCA9IHJhbmtlZF9nZW5lX2xpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3NldHMgPSBnZW5lX3NldHNbLCBjKCJnc19uYW1lIiwgImVuc2VtYmxfZ2VuZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdTRUFfcF92YWxfdGhyZXNoID0gMSkKCmxpc3RfcmVzdWx0c1tbIkhTX3ZzX0hEX2dzZWEiXV0gPSBnc2VhX3Jlc3VsdHMKCmdzZWFfcmVzdWx0c0ByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihwdmFsdWUgPCAwLjA1KSAlPiUKICBkcGx5cjo6dG9wX24oLiwgbiA9IDIwMCwgd3QgPSBhYnMoTkVTKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b29fbG9uZyA9IGlmZWxzZShuY2hhcihJRCkgPiA2MCwgeWVzID0gVFJVRSwgbm8gPSBGQUxTRSkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBzdHJpbmdyOjpzdHJfc3ViKElELCBlbmQgPSA2MCkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBpZmVsc2UodG9vX2xvbmcsIHllcyA9IHBhc3RlMChJRCwgIi4uLiIpLCBubyA9IElEKSkgJT4lCiAgYXF1YXJpdXM6OmdzZWFfcGxvdChzaG93X2xlZ2VuZCA9IFRSVUUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkdTRUEgdXNpbmcgYWxsIGdlbmVzIChjb3VudCBtYXRyaXgpIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkKYGBgCgpXZSBjb21wdXRlIGEgZm9sZCBjaGFuZ2UgdGFibGUgdG8gbWFrZSBhIHdvcmRjbG91ZCA6CgpgYGB7ciBmb2xkX2NoYW5nZV9pZmVnc19oc192c19oZH0KZm9sZF9jaGFuZ2UgPSBnZW5lX2NvcnJlc3AKY29sbmFtZXMoZm9sZF9jaGFuZ2UpID0gYygiZ2VuZV9uYW1lIiwgIklEIikKcm93bmFtZXMoZm9sZF9jaGFuZ2UpID0gZm9sZF9jaGFuZ2UkSUQKZm9sZF9jaGFuZ2UkRkMgPSByYW5rZWRfZ2VuZV9saXN0W3Jvd25hbWVzKGZvbGRfY2hhbmdlKV0KZm9sZF9jaGFuZ2UkcGN0LjEgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzb2JqLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpWywgc29iakBhY3RpdmUuaWRlbnQgPT0gIkhTIl0gJT4lCiAgYXBwbHkoLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24ob25lX3JvdykgewogICAgcmV0dXJuKCBtZWFuKG9uZV9yb3cgIT0gMCkgKQogIH0pCmZvbGRfY2hhbmdlJHBjdC4yID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJjb3VudHMiKVssIHNvYmpAYWN0aXZlLmlkZW50ID09ICJIRCJdICU+JQogIGFwcGx5KC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKG9uZV9yb3cpIHsKICAgIHJldHVybiggbWVhbihvbmVfcm93ICE9IDApICkKICB9KQpmb2xkX2NoYW5nZSRGQ194X3BjdCA9IGlmZWxzZShmb2xkX2NoYW5nZSRGQyA+IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9IGZvbGRfY2hhbmdlJEZDICogZm9sZF9jaGFuZ2UkcGN0LjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gZm9sZF9jaGFuZ2UkRkMgKiBmb2xkX2NoYW5nZSRwY3QuMikKCmRpbShmb2xkX2NoYW5nZSkgOyBoZWFkKGZvbGRfY2hhbmdlKQpgYGAKCldlIG1ha2UgdGhlIGdzZWEgcGxvdCBmb3Igc29tZSBnZW5lIHNldHMgOgoKYGBge3IgZ3NlYV9wbG90X3dpdGhpbl9pZmVnc19oc192c19oZCwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyNX0KZ3Nfb2kgPSBjKCJHT01GX1NUUlVDVFVSQUxfQ09OU1RJVFVFTlRfT0ZfUklCT1NPTUUiLAogICAgICAgICAgIkdPQlBfQl9DRUxMX0NIRU1PVEFYSVMiLAogICAgICAgICAgIkdPQlBfQVVUT0NSSU5FX1NJR05BTElORyIsCiAgICAgICAgICAiR09CUF9DT01QTEVNRU5UX0FDVElWQVRJT04iLAogICAgICAgICAgIkdPQlBfQ09STklGSUNBVElPTiIsCiAgICAgICAgICAiV1BfSEFJUl9GT0xMSUNMRV9ERVZFTE9QTUVOVF9PUkdBTk9HRU5FU0lTX1BBUlRfMl9PRl8zIiwKICAgICAgICAgICJQSURfSU5URUdSSU4zX1BBVEhXQVkiKQoKcGxvdF9saXN0ID0gbWFrZV9nc2VhX3Bsb3QoZ3NlYV9yZXN1bHRzLCBnc19vaSwgZm9sZF9jaGFuZ2UsICJGQ194X3BjdCIpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gMiwgd2lkdGhzID0gYygyLDEuNSkpCmBgYAoKIyBTYXZlCgpXZSBzYXZlIHRoZSBsaXN0IG9mIHJlc3VsdHMgOgoKYGBge3Igc2F2ZV9saXN0X3Jlc3VsdHN9CnNhdmVSRFMobGlzdF9yZXN1bHRzLCBmaWxlID0gcGFzdGUwKG91dF9kaXIsICIvIiwgc2F2ZV9uYW1lLCAiX2xpc3RfcmVzdWx0cy5yZHMiKSkKYGBgCgoKIyBSIFNlc3Npb24KCmBgYHtyIHNlc3Npb25pbmZvLCBlY2hvID0gRkFMU0UsIGZvbGRfb3V0cHV0ID0gVFJVRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==